/*
 * GeoVISTA Center (Penn State, Dept. of Geography)
 * Copyright (c), 2003-2011,  Jin Chen and Alan M. MacEachren, GeoVISTA Center, Penn State University
 * Licensed under Simplified BSD License
 *
 *
 * @author: jin Chen 
 * @date: Nov 17, 2005$
 * @version: 1.0
 */
package edu.psu.geovista.app.gmatrix.canvas;

import edu.psu.geovista.app.gmatrix.control.BasicSelectClient;
import edu.psu.geovista.app.gmatrix.data.BasicMatrixmodel;
import edu.psu.geovista.app.gmatrix.event.MatrixIndicationListener;
import edu.psu.geovista.app.gmatrix.event.MatrixSelectionEvent;
import edu.psu.geovista.app.gmatrix.event.MatrixSelectionListener;
import edu.psu.geovista.app.gmatrix.toolkit.*;
import edu.psu.geovista.app.vit.pub.utils.VitUtils;
import epoviz.trex.common.j2d.mouse.RectangleMouseTracker;
import epoviz.trex.common.j2d.mouse.ShapePainter;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;

public class DfMatrixCanvas extends JPanel implements MouseSelectTarget, ShapePainter,MatrixSelectionListener {
    public static final String CHANGE_SIZE="change size";
    private static final Logger logger=Logger.getLogger(DfMatrixCanvas.class.getName());

    public static final int  LAYOUT_TOP_BOTTOM=1;
    public static final int  LAYOUT_BOTTOM_TOP=2;
    public static final int RATIO_FULLSIZE=1;
    //GUI
    private int layouttype=LAYOUT_BOTTOM_TOP;//LAYOUT_TOP_BOTTOM;//LAYOUT_BOTTOM_TOP;

    protected GridLine gridline;
    Dimension cellSize;
    boolean style3d=false; //if true, all cells painted in 3D style
 
    protected Map popupAction;
    protected BasicMatrixmodel datamodel;  //data model
   // JMatrix matrix;

    /**
     * Determine how Celles layout: usally GridLayout layout compoenents from top ( 1st row) to bottom. This is the case LAYOUT_TOP_BOTTOM
     * But a Legend usually need to layout from bottom ( represent lower y value) to top. So by setting layouttype=LAYOUT_BOTTOM_TOP,
     * legendControl's color scheme will = interactiveLegend's color scheme
     */

    private Set<Point> selectedCell=new HashSet<Point>(0); // 
    Point highlightedCell; //index of highlighted cell

    private Shape shape;// 



    //tools

    PropertyChangeSupport pspt;
    private FireEventSupport evtfirer;
    BasicSelectClient selectClient;//any selection outcome will be passed outside via this interface
    MouseSelectSupport selectSupport;
    DfHighlightSupport highlightSupport;
    protected DfMatrixCanvas.MyRectangleMouseTracker mousetracker;




    public DfMatrixCanvas(BasicMatrixmodel datamodel) {
        this.datamodel=datamodel;
        init();
    }
   

    /*******************************************************************************************************
     *                init
     *******************************************************************************************************/
    private void init(){

        //this.cellfactory=new CellFactory();
        //this.datamodel =new DfMatrixModel();//new SimpleMatrixmodel();
        //this.context =new MatrixGuiContext();
        this.gridline =new GridLine();

        mousetracker = new MyRectangleMouseTracker();
        //MyRectangleMouseTracker mt=mousetracker;
        mousetracker.setTarget(this);

        selectSupport =new DfMouseSelectSupport();
        selectSupport.setTarget(this);
        highlightSupport=new DfHighlightSupport();
        // selectSupport.setPolicy(DfMouseSelectionSupport.POLICY_INCREMENTAL);

        this.addComponentListener(new ComponentAdapter(){//reset boundary of swatches
            public void componentResized(ComponentEvent e) {
                //System.out.print("componentResized()");
                resetLayout();

            }
        });

        this.pspt =new PropertyChangeSupport(this);


    }

    public  void resetLayout(){
        logger.fine("");
        Dimension size = this.getSize();// 
        ComponentOrientation compOrt = this.getComponentOrientation();
        setComponentOrientation(compOrt);
        Dimension cellSize =this.computeCellSize(size);
        setCellSize(cellSize);
        this.repaint(); // 

        //below are those independent objs, can instantiate anytime
        this.pspt.firePropertyChange(new PropertyChangeEvent(this,CHANGE_SIZE,null,cellSize) );
        //test
        if(logger.isLoggable(Level.FINER) ){
            printRowColCenters();
        }


    }

    public void printRowColCenters() {
        Dimension cellSize = this.getCellSize();
        GridLine gridline = this.getGridline();
        System.out.println("    Cell size:"+cellSize);
        System.out.println("    grid size:"+gridline.getSize() );
        System.out.println("     Row centers: ");
        double[] rowctrs = this.getRowCenters();
        for (int i=0;i<rowctrs.length ;i++){
            System.out.println("        "+rowctrs[i]);
        }

        System.out.println("    Column centers: ");
        double[] colctrs = this.getColumnCenters() ;
        for (int i=0;i<colctrs.length ;i++){
            System.out.println("        "+colctrs[i]);
        }
    }

    /*******************************************************************************************************
     *             UI & Size related
     *******************************************************************************************************/
    /**
     * Listen canvas component size change
     * @param l
     */
    public void addPropertyChangeListener(PropertyChangeListener l){
        this.pspt.addPropertyChangeListener(l);
    }
    public   Cell getCell(int row, int col) {
       /* Symbol symbol = this.datamodel.getSymbol(row, col);
        Cell cell = this.cellfactory.createCell(symbol);
        cell.setIndex(row,col);
        cell.setBackground(this.datamodel.getBackground(row,col) );
        cell.setForeground(this.datamodel.getForeground(row,col));*/
        Cell cell=datamodel.getCell(row,col);

          // 
        cell.setBounds(this.computeCellBound(row,col));
        cell.setSymbolBoundary(this.computeSymbolBoundary(row,col));
        cell.setStyle3d(this.isStyle3d() );

        return cell;
    }


    /*public double[] getRowCenters(){
        //logger.fine("");
        int numrow = this.getNumOfRow();
        double ctrs[]=new double[numrow];
        for (int i=0;i<numrow;i++){
            //Rectangle r = this.computeCellBound(i, 0);
            Point ctr = this.getCellCenter(new Point(i, 0));
            ctrs[i]=ctr.y;
        }
        return ctrs;
    }
    public double[] getColCenters(){
        //logger.fine("");
        int numcol = this.getNumOfColumn();
        double ctrs[]=new double[numcol];
        for (int i=0;i<numcol;i++){
            //Rectangle r = this.computeCellBound(i, 0);
            Point ctr = this.getCellCenter(new Point(0,i));
            ctrs[i]=ctr.x;
        }
        return ctrs;
    }*/
   /* public Symbol getSymbol(int row,int col) {
      return this.datamodel.getSymbol(row,col);
   }*/
    public int getNumOfColumn() {
        return datamodel.getNumOfCulumn() ;
    }



    public int getNumOfRow() {
        return datamodel.getNumOfRow();
    }

    public boolean isStyle3d() {
        return style3d;
    }

    public void setStyle3d(boolean style3d) {
        this.style3d = style3d;
    }






    public Dimension getCellSize() {
        return cellSize;
    }

    public void setCellSize(Dimension cellSize) {
        this.cellSize = cellSize;
    }


    public GridLine getGridline() {
        return gridline;
    }

    public void setGridline(GridLine gridline) {
        this.gridline = gridline;
    }

    public int getLayouttype() {
        return layouttype;
    }

    public void setLayouttype(int layouttype) {
        this.layouttype = layouttype;
    }





    /****************************************************
     *                runtime calculated attributes
     *****************************************************/
    /**
     *
     * @param size   canvas size
     * @return       cell size
     */
    public Dimension computeCellSize(Dimension size){

        Dimension gap = gridline.getSize();
        int numOfRow = getNumOfRow();
        int numOfCulumn = getNumOfColumn();
        if(numOfRow==0||numOfCulumn==0){
            return new Dimension();
        }
        int h = (size.height-gap.height*(numOfRow -1))/getNumOfRow();

        int w = (size.width-gap.width *(numOfCulumn -1)) /getNumOfColumn();
        logger.fine("cell size:["+h+","+w+"]");
        return new Dimension(w,h);

    }

    /**
     *  remove  Dimension swatchSize

     * @param arow     index of swatch, map to colors' index
     * @param acol
     * @return  r
     */
    public  Rectangle computeCellBound(int arow, int acol){
        Dimension cellSize=this.getCellSize() ;
        /* if(cellSize.width <0||cellSize.height <0){
            logger.severe("Matrix cell size <0 ");
        }*/
        int row=0, column=acol;
        Dimension gap = gridline.getSize();
        if(this.layouttype==LAYOUT_BOTTOM_TOP){
            row=getNumOfRow() -arow -1;//1st swatch in bottom row( 1st row is on top)

        }
        else {
            row=arow;
        }

        int x;
        if ((this.getComponentOrientation().isLeftToRight()) ) {
            x = column * (cellSize.width + gap.width);
        } else {
            x = (getNumOfColumn() - column - 1) * (cellSize.width + gap.width);
        }
        int y = row * (cellSize.height + gap.height);
        return new Rectangle(x,y,cellSize.width ,cellSize.height );
    }

    /**
     * unselected cell paint in half size
     * @param row
     * @param col
     * @return dsfsd
     */
    public float getSymbolSizeRatio(int row, int col){
        float fullsize=RATIO_FULLSIZE;
        float halfsize=RATIO_FULLSIZE/2.0f;
        float sizeratio=fullsize;
        //2. found if there is selection
        boolean hasSelection=false;//true if there is some swatch in selected mode
        if(this.getSelectedCell().size() >0){
            hasSelection=true;
        }
        if(hasSelection){
            if(isSelected(row,col) ){
                //MatrixLoggerMgr.fine(logger,"getSymbolSizeRatio() compute full size for selected cell ["+row+","+col+"]");
               // LoggerMgr.fine(logger,"has selection. Cell["+row+","+col+"] is selected, thus fullsize ");
                sizeratio=fullsize;


            }
            else{
                //LoggerMgr.fine(logger,"has selection. Cell["+row+","+col+"] is NOT selected , thus halfsize ");
                sizeratio=halfsize;//paintComponent(g2d,Swatch.PAINT_MODE_REDUCED);

            }
        }
        else {
            //LoggerMgr.fine(logger,"has on selection, all cell full size");
            //MatrixLoggerMgr.fine(logger,"All selected, getSymbolSizeRatio() compute full size for all");
            sizeratio=fullsize;
            
        }
        return sizeratio;
    }

    public Rectangle computeSymbolBoundary(int row, int col) {
        float symbolSizeRatio = this.getSymbolSizeRatio(row, col);
        Rectangle cellbound = this.computeCellBound(row, col);
        int w = (int) (cellbound.width * symbolSizeRatio);
        int h = (int) (cellbound.height * symbolSizeRatio);
        return new Rectangle(cellbound.x, cellbound.y,w,h);
    }



    public boolean isSelected(int row, int col) {
        Point cellindex=new Point(row,col);
        Set<Point> selectedCell = this.getSelectedCell();
        for (Iterator iterator = selectedCell.iterator(); iterator.hasNext();) {
            Point aindex = (Point) iterator.next();
            if(aindex.equals(cellindex) ){
                return true;
            }

        }
        return false;

    }




    /*******************************************************************************************************
     *                services
     *******************************************************************************************************/
    /**************************************************
     *                search location
     ***************************************************/
    /**
     *
     * @return  each element is the center location of a cell of a row
     */
    public double[] getRowCenters(){

        int numOfColumn = this.getNumOfColumn();
        double[] xs =new double[numOfColumn];
        for (int col =0;col <numOfColumn;col++){
            //this.computeCellBound(0,col);
            Rectangle rect = this.computeCellBound(0, col);
            xs[col] = rect.getCenterX();
        }
        return xs;
    }
    /**
     *
     * @return  each element is the center location of a cell of a column
     */
    public double[] getColumnCenters(){

        int numOfRow = this.getNumOfRow();
        //Point2D[] ps=new Point2D[numOfCulumn];
        double[] ys=new double[numOfRow];

        for (int row =0;row <numOfRow;row++){
            //this.computeCellBound(0,row);
            Rectangle rect = this.computeCellBound(row, 0);
            ys[row] = rect.getCenterY();
        }
        return ys;
    }

    /**
     * todo need reimplemnt
     * found a Cell given any screen location(x,y) on panel
     * @param x    screen location.x
     * @param y    screen location.y
     * @return s
     */
    public Cell getCellByLocation(int x, int y){
        Point cellIndex = getCellIndexByLocation(x, y);
        if(cellIndex==null)return null;
        else{
            return getCell(cellIndex.x,cellIndex.y);
        }
    }
    /**
     * todo
     * @param x       screen x
     * @param y       screen y
     * @return        cell index
     */
    private Point getCellIndexByLocation(int x, int y) {
        boolean contain =false;
        int numOfCulumn = getNumOfColumn();
        int numOfRow = getNumOfRow();
        for (int row =0;row <numOfRow;row++){
            for (int col =0;col <numOfCulumn;col++){
                Rectangle bd = computeCellBound( row, col);
                contain = bd.contains(x,y);
                if(contain) return new Point(row,col);
            }
        }
        return null;
    }

    /**
     * todo need reimplemnt
     * @param r
     * @return   cells within/intersect by the rectangle r
     */
    public Set <Point> getCellesIn(Rectangle r){
        Set  <Point> selected=new HashSet<Point>();
        // boolean contain =false;
        int numOfCulumn = getNumOfColumn();
        int numOfRow = getNumOfRow();
        for (int row =0;row <numOfRow;row++){
            for (int col =0;col <numOfCulumn;col++){
                //Swatch aSwatch = swatches[i][j];
                Rectangle aCellBound = computeCellBound( row, col);
                if(r.contains(aCellBound)||
                        r.intersects(aCellBound)){
                    selected.add(new Point(row,col));
                }
            }
        }
        return selected;
    }
      public Point getCellCenter(Point index){
        Rectangle bd = computeCellBound(index.x, index.y);
        int x= (int) bd.getCenterX();
        int y=(int) bd.getCenterY();
        return new Point(x,y);
    }

    /*******************************************************************************************************
     *                setter/getter
     *******************************************************************************************************/
    /**
     * set number of nbrow and column
     * will override foreground color[][]
     * @param nbrow
     * @param nbcol
     */
    public void setRowColumn( int nbrow, int nbcol){
      /*  this.setNumOfRow(nbrow) ;
        this.setNumOfCulumn(nbcol);
        this.setForegrounds(null);//replace current cell colors*/
        datamodel.setRowColumn(nbrow,nbcol);
        this.resetLayout();
        //repaint();
    }
    /**
     *
     * @param colors    
     */
    public void setColors(Color[][] colors) {
        /*setNumOfRow(colors.length) ;
        setNumOfCulumn(colors[0].length) ;
        this.setForegrounds(colors);*/
        datamodel.setColors(colors);
        this.resetLayout();
        // repaint();*/
    }


    public void setColors(Color[] colors) {
       /* this.setNumOfRow(1) ;
        this.setNumOfCulumn(colors.length) ;
        Color[][] mycolors=new Color[1][colors.length ];
        mycolors[0]=colors;
        setForegrounds(mycolors);*/
        datamodel.setColors(colors);
        this.resetLayout();
        repaint();
    }
     /**
     *  
     * @param hgap
     * @param vgap
     */
    public void setGridSize(int hgap, int vgap) {
        Dimension gap = this.gridline.getSize();
        gap.setSize(hgap,vgap);
        this.resetLayout();
        repaint();
    }
    public void setGridlineColor(Color c){
        this.gridline.setColor(c);
    }

    public void setSelectClient(BasicSelectClient selectClient) {
        this.selectClient = selectClient;
    }

    public Map getPopupAction() {
        return popupAction;
    }

    public void setPopupAction(Map popupAction) {
        this.popupAction = popupAction;
    }
    /*******************************************************************************************************
     *      move up down: these methods must be within the canvas since it need to move selected rows
     *******************************************************************************************************/



    /*******************************************************************************************************
     *                 events
     *******************************************************************************************************/
    /**
     *
     * @param l
     */
    public void addMatrixSelectionChangeListener(MatrixSelectionListener l){
        this.getEvtfirer().addListener(MatrixSelectionListener.class, l);
    }
    public void addMatrixIndicationChangeListener(MatrixIndicationListener l){
        this.getEvtfirer().addListener(MatrixIndicationListener.class, l);
    }
    protected void fireSelectionChange(Set <Point> selection){
        this.getEvtfirer().fireSelectionChanged(selection);
    }
    protected void fireIndicationChanged(Point indication){
        this.getEvtfirer().fireIndicationChanged(indication);
    }



    /*******************************************************************************************************
     *                selection     highlight
     *******************************************************************************************************/
    /**************************************************
     *                highlight
     ***************************************************/

    /**
     *
     * @param x    screen location
     * @param y
     */
    public void mouseHighlight(int x, int y){
        Point index = this.getCellIndexByLocation(x, y);
        /*if(highlightedCell==null) {
            highlightedCell=index;
        }*/
        if(index!=null&&! index.equals(highlightSupport.getIndex()) ){
            //highlightedCell.setLocation(index);
            highlightSupport.setIndex(index);
            repaint();
            this.fireIndicationChanged(index);
        }
    }

    /**
     * exteneral highlight
     * @param index
     */
    public void showHighlight(Point index){
        if(index!=null&&! index.equals(highlightSupport.getIndex()) ){
            //highlightedCell.setLocation(index);
            highlightSupport.setIndex(index);
            repaint();
            // this.fireIndicationChanged(index);
        }
    }

    /**
     *
     * @return h
     */
    /*  public Point getHighlightedCell() {
        return highlightedCell;
    }*/

    /*  public void setHighlightedCell(Point highlightedCell) {
        this.highlightedCell = highlightedCell;
    }*/









    /*******************************************************************************************************
     *                select
     *******************************************************************************************************/
     /**************************************************
      *                primary
      ***************************************************/
    /**
     *  return rows contain selected cells
     * @return index of rows which have selected cell. The array is sorted
     */
    public int[] getSelectedRows(){
        Set <Integer> rows=new HashSet();
        Set<Point> selectedCells = getSelectedCell();
        for (Iterator iterator = selectedCells.iterator(); iterator.hasNext();) {
            Point point = (Point) iterator.next();
            rows.add(point.x);
        }
        int[] rowIndexs = VitUtils.convert2IntArray(rows);
        Arrays.sort(rowIndexs);
        //int[] test=new int[3];
        return rowIndexs;
    }
    /**
     *
     * @return
     */
    public Set<Point> getSelectedCell() {
        //return selectedCell;
        return this.selectSupport.getTotalSelection();
    }



    /**
     * set and display given selection. This method not fire selection event.
     * Can only be used for external selecting.
     * For internal select, use mouse select
     * @param matrixIndexs
     *
     */
   /* public void selectExternally(Set<Point> matrixIndexs){

        setSelectedCell(matrixIndexs);
        repaint();


    }
    public void selectInternally(Set<Point> matrixIndexs){
        setSelectedCell(matrixIndexs);
        repaint();
        fireSelectionChange(this.getSelectedCell() );//


    }*/

    public  void select(Set<Point> select, boolean fireevent){
      //todo  if current total selection equal to given one, do nothing.
        setSelectedCell(select);
        repaint();
        if(fireevent){
            selectClient.select(this.getSelectedCell());
             //fireSelectionChange(this.getSelectedCell() );//
        }

    }
     /**
     * not call this directly, call selectExternally or  selectInternally instead
     * @param selectedCell
     */
    protected  void setSelectedCell(Set<Point> selectedCell) {
        /*this.selectedCell.clear();
        this.selectedCell.addAll(selectedCell);*/
        selectSupport.setTotalSelection(selectedCell);

    }

    /**
     * For exteranl call, not fire event; for interanl call, fire event
     * @param matrixIndexs
     * @param fireevent
     */
    public void selectAdd(Set<Point> matrixIndexs, boolean fireevent){
        selectSupport.addSelection(matrixIndexs);
        Set total = selectSupport.getTotalSelection();
        select(total,fireevent);
        /*selectSupport.addSelection(matrixIndexs);
        repaint();*/
    }

    /**
     * For exteranl call, not fire event; for interanl call, fire event
     * @param matrixIndexs
     * @param fireevent
     */
    public void substractSelect(Set<Point> matrixIndexs, boolean fireevent){
        selectSupport.substractSelection(matrixIndexs);
        Set total = selectSupport.getTotalSelection();
        select(total,fireevent);

        //selectSupport.substractSelection(matrixIndexs);
        //repaint();
    }
    public void clearSelection() {
        logger.finer("clearSelection");
        //this.getSelectedCell().clear();
        selectSupport.clearSelection();

    }

    /**************************************************
     *                convenient methods
     ***************************************************/

    /**
     *
     * @param rowIndexs
     * @param fireevent
     */
     public void selectRow(int[] rowIndexs, boolean fireevent){
        Set<Point> selects = getRowsSelection(rowIndexs);
        select(selects,fireevent);
     }
    /**
     *
     * @param rowIndex       row index of selected row
     * @param fireevent
     *
     */
    public void selectRow(int rowIndex, boolean fireevent){
        Set<Point> selects = getRowSelection(rowIndex);
        select(selects,fireevent);
    }
    private Set<Point> getRowsSelection(int[] rowIndexs) {
        int nbOfColumn=this.getNumOfColumn();
        int total=nbOfColumn*rowIndexs.length ;
        Set <Point> selects=new HashSet<Point>(total);
        //Point selects[]=new Point[nbOfColumn];
        for (int i =0;i <rowIndexs.length ;i++){
            //System.out.println("Select row:"+rowIndexs[i]);
            for (int col =0;col <nbOfColumn;col++){
                Point cell = new Point(rowIndexs[i], col);
                selects.add(cell);
            }
        }
        return selects;
    }
    private Set<Point> getRowSelection(int rowIndex) {
        int nbOfColumn=this.getNumOfColumn();
        Set <Point> selects=new HashSet<Point>(nbOfColumn);
        //Point selects[]=new Point[nbOfColumn];
        for (int col =0;col <nbOfColumn;col++){
            Point cell = new Point(rowIndex, col);
            selects.add(cell);
        }
        return selects;
    }

    public void selectColumn(int colIndex, boolean fireevent){
        Set<Point> selects = getColSelection(colIndex);
        select(selects,fireevent);

    }

    private Set<Point> getColSelection(int colIndex) {
        int nbOfrow =this.getNumOfRow();
        Set <Point> selects=new HashSet<Point>(nbOfrow);
        for (int row =0;row <nbOfrow;row++){
            Point cell = new Point(row,colIndex);
            selects.add(cell);

        }
        return selects;
    }


    /**
     * For exteranl call, not fire event; for interanl call, fire event
     * @param rowIndex
     * @param fireEvent
     */
    public void addRowSelect(int rowIndex, boolean fireEvent){
        Set<Point> rs = this.getRowSelection(rowIndex);
        this.selectAdd(rs,fireEvent);
    }

    /**
     * For exteranl call, not fire event; for interanl call, fire event
     * @param rowIndex
     * @param fireEvent
     */
    public void substractRowSelect(int rowIndex, boolean fireEvent){
        Set<Point> rs = this.getRowSelection(rowIndex);
        this.substractSelect(rs,fireEvent);
    }
    /**************************************************
     *  mouse select: for interanl GUI select,
     *  must be seperated from external select
     ***************************************************/

    public void mouseSelect(Set<Point> totalSel, Set<Point> curSel, boolean selectingFinish) {

        if(totalSel ==null||totalSel.size() ==0){

            return;
        }
        //fireSelectionChange(this.getSelectedCell() );//
        selectClient.select(this.getSelectedCell());
        logger.fine("GUI select:"+totalSel.size() +" cells, FIRE events");
       // select(totalSe)
        /*this.selectedCell.clear();
        this.selectedCell.addAll(totalSel);*/
        //showSelection(totalSel); // 


    }



    public void mouseClearSelection() {

       // this.clearSelection();//todo fire event?


    }
    /*    public void updateGUI() {
        this.repaint();
    }*/
    public Set <Point> findMouseSelection(Point init, Point last, Point current) {
        Point startp=init;
        Point endp=current;
        Rectangle r=new Rectangle (startp, new Dimension(endp.x-startp.x,endp.y-startp.y));

        Set  <Point> selected=new HashSet<Point>();
        // boolean contain =false;
        int numOfCulumn = getNumOfColumn();
        int numOfRow = getNumOfRow();
        for (int row =0;row <numOfRow;row++){
            for (int col =0;col <numOfCulumn;col++){

                Rectangle aswatchBound = this.computeCellBound( row, col);
                if(r.contains(aswatchBound)||
                        r.intersects(aswatchBound)){
                    selected.add(new Point(row,col));
                }
            }
        }
        return selected;

        //return r;  //To change body of implemented methods use File | Settings | File Templates.
    }


    /*******************************************************************************************************
     *                support: paint...
     *******************************************************************************************************/

    /**
     *
     */
    public void repaint(){
        super.repaint();
    }

    public void paintComponent(final Graphics g) {
        super.paintComponent(g);
        //paint(g);
    }

    public  void paint(Graphics g) {
        super.paint(g);
        Graphics g2d=g;
        Color originalColor = g2d.getColor();

        //1. paint background color (= gap color)
        g2d.setColor(getBackground());
        g.fillRect(0,0,getWidth(), getHeight());
        //g2d.fill3DRect(0,0,getWidth(), getHeight(),true);

        Set highlight=new HashSet();

        //3. paint cells.
        //  
        //       

        int numOfCulumn = getNumOfColumn();
        int numOfRow = getNumOfRow();
        for (int row =0;row <numOfRow;row++){
            for (int col =0;col <numOfCulumn;col++){

                final Cell cell = this.getCell(row,col);
                /*   if(logger.isLoggable(Level.FINE ) ){
                    System.out.print("cell:"+cell.getBounds()+" ;");
                }*/

                cell.paint(g2d);  //todo  

            }
        }

        g2d.setColor(originalColor);
        painttShape((Graphics2D) g);//paint mouse tracker

        Point highlightcell = highlightSupport.getIndex();
        Point cellCenter = this.getCellCenter(new Point(highlightcell.x,highlightcell.y));
        //System.out.println("highlight:"+highlightcell+" at "+cellCenter);
        this.highlightSupport.draw(g,cellCenter);
    }


    /*  public void setLayouttype(int layouttype) {
        this.context.setLayouttype(layouttype);
    }
    public int getNumOfRow() {
        return context.getNumOfRow();
    }

    public int getNumOfCulumn() {
        return context.getNumOfCulumn();
    }*/
    public static Color[][] convert(int[][] values){
        int row=values.length ;
        int col=values[0].length/3 ;

        Color[][] mycolors=new Color[row][col];

        for (int i=0;i<row ;i++){//rows
            int[] rowv=values[i];
            Color[] colors=new Color[col];
            for (int j=0;j<col ;j++){
                colors[j] = new Color( rowv[(j*3)], rowv[(j*3)+1], rowv[(j*3)+2] );
                //System.out.println("a color:"+colors[j]);
            }
            mycolors[i]=colors;
        }
        return mycolors;

    }


    /*******************************************************************************************************
     *                mouse
     *******************************************************************************************************/
    /**************************************************
     *                mouse path tracker
     ***************************************************/
    public void setShape(Shape shp) {
        shape=shp;
    }

    public void painttShape(Graphics2D g2d) {
        if(shape!=null){
            Color oldc = g2d.getColor();
            g2d.setColor(Color.black );

            g2d.draw(shape);
            g2d.setColor(oldc);

        }
    }
    public void clear() {
        shape=null;
    }

    public FireEventSupport getEvtfirer() {
        if(evtfirer==null){
            evtfirer=new FireEventSupport(this);
        }
        return evtfirer;
    }

    public void setEvtfirer(FireEventSupport evtfirer) {
        this.evtfirer = evtfirer;
    }

   /* public Color[][] getBackgrounds() {
        return backgrounds;
    }

    public Symbol[][] getSymbols() {
        return symbols;
    }*/

    /*******************************************************************************************************
     *                getter setter
     *******************************************************************************************************/
    public void setColnames(String[] colnames) {
        this.highlightSupport.setColnames(colnames);
    }



    public void setRownames(String[] rownames) {
        this.highlightSupport.setRownames(rownames);
    }



    public void matrixSelectionChanged(MatrixSelectionEvent e) {
        Set<Point> selection = e.getSelection();
        if(selection==null)return;
        this.select(selection,false);//external selection, not fire event
    }
    //return popup;


    /*******************************************************************************************************
     *                inner classes
     *******************************************************************************************************/
  
    class MyRectangleMouseTracker extends RectangleMouseTracker {
        Rectangle r=new Rectangle();
        public void mouseMoved(MouseEvent event){
            super.mouseMoved(event);
            Point point = event.getPoint();
            mouseHighlight(point.x,point.y);
        }

        public void mousePressed(MouseEvent event) {
            super.mousePressed(event);
            int button = event.getButton();
            if(button==MouseEvent.BUTTON1&&!event.isShiftDown()) {// 
                //  clearSelection(); //clear last selection
                r.setBounds(0,0,0,0); // 
                //repaint();
            }
        }

        public void mouseDragged(MouseEvent e) {
            //System.out.println("isControlDown:"+e.isControlDown());
            //if(!e.isControlDown()){// 
            super.mouseDragged(e);
            // 
            // 
            r.setBounds(this.getRectangle());
            //}
        }
        public void mouseReleased(MouseEvent e){

            super.mouseReleased(e);
            int button = e.getButton();
            if(button==MouseEvent.BUTTON1) {// 

                /* Set<Swatch> selected = getSwatchesIn(r);
                tryAddSelection(selected, false);
               fireSelectionChange();
                }*/
            }

        }

        /**
         *  for popup menu
         * @param e
         */
        public void mouseClicked(java.awt.event.MouseEvent e) {
            
        }//methods



    }//inner class



   /*******************************************************************************************************
     *                test
     *******************************************************************************************************/
    public void printRowColumnCenters(){

    }


}